package priv.pfz.sbv;

import com.alibaba.fastjson.JSONArray;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @author yyjxp
 * 2024/2/16 10:19
 */
public class InitTimelineGenerator {

    public static void generate(Visualizer visualizer) {
        //获取bean初始化埋点
        List<CreateBeanOpData> opDataList = CreateBeanOpData.get();
        Map<String, Map<CreateBeanOpType, Long>> beanNameMap = Maps.newLinkedHashMap();
        for (CreateBeanOpData opData : opDataList) {
            if (visualizer.isExcludeSpringInnerBean() && Utils.isSpringInnerBean(opData.getBeanName())) {
                continue;
            }
            Map<CreateBeanOpType, Long> opMap = beanNameMap.computeIfAbsent(opData.getBeanName(), k -> Maps.newHashMap());
            opMap.put(opData.getOpType(), opData.getTime());
        }
        long epoch = opDataList.isEmpty() ? 0 : opDataList.get(0).getTime();

        //bean初始化时序转换为json
        JSONArray categories = new JSONArray();
        JSONArray placeholder1 = new JSONArray();
        JSONArray create = new JSONArray();
        JSONArray placeholder2 = new JSONArray();
        JSONArray instantiate = new JSONArray();
        JSONArray placeholder3 = new JSONArray();
        JSONArray populate = new JSONArray();
        List<Map.Entry<String, Map<CreateBeanOpType, Long>>> entries = Lists.newArrayList(beanNameMap.entrySet());
        Collections.reverse(entries);
        for (Map.Entry<String, Map<CreateBeanOpType, Long>> entry : entries) {
            String beanName = entry.getKey();
            Map<CreateBeanOpType, Long> opMap = entry.getValue();
            if (opMap.size() < CreateBeanOpType.values().length) {
                continue;
            }
            categories.add(beanName);

            // epoch
            // --placeholder1--
            // createBegin
            // --create--
            // createEnd
            placeholder1.add(getTime(opMap, epoch, CreateBeanOpType.CREATE_BEGIN));
            create.add(getTime(opMap, CreateBeanOpType.CREATE_BEGIN, CreateBeanOpType.CREATE_END));

            // epoch
            // --placeholder2--
            // instantiateBegin
            // --instantiate--
            // instantiateEnd
            // --placeholder3--
            // populateBegin
            // --populate--
            // populateEnd
            placeholder2.add(getTime(opMap, epoch, CreateBeanOpType.INSTANTIATE_BEGIN));
            instantiate.add(getTime(opMap, CreateBeanOpType.INSTANTIATE_BEGIN, CreateBeanOpType.INSTANTIATE_END));
            placeholder3.add(getTime(opMap, CreateBeanOpType.INSTANTIATE_END, CreateBeanOpType.POPULATE_BEGIN));
            populate.add(getTime(opMap, CreateBeanOpType.POPULATE_BEGIN, CreateBeanOpType.POPULATE_END));
        }

        //将json填充到模板中
        String imports = Utils.readResource(visualizer.isGenerateLocalHtml() ? "echarts.min.js" : "echarts_imports.html");
        Map<String, String> params = ImmutableMap.of(
                "imports", imports,
                "categories", categories.toJSONString(),
                "placeholder1", placeholder1.toJSONString(),
                "create", create.toJSONString(),
                "placeholder2", placeholder2.toJSONString(),
                "instantiate", instantiate.toJSONString(),
                "placeholder3", placeholder3.toJSONString(),
                "populate", populate.toJSONString()
        );
        String filename = Utils.getFilename(visualizer.getHtmlPath(), "init_timeline.html");
        Utils.writeFile("init_timeline_template.ftl", params, filename);
    }

    private static double getTime(Map<CreateBeanOpType, Long> opMap, long epoch, CreateBeanOpType to) {
        return (double) Math.round(((double) opMap.get(to) - epoch) / 1000_0) / 100;
    }

    private static double getTime(Map<CreateBeanOpType, Long> opMap, CreateBeanOpType from, CreateBeanOpType to) {
        return (double) Math.round(((double) opMap.get(to) - opMap.get(from)) / 1000_0) / 100;
    }
}
